/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/types.hpp>
#include <baja/app/app.hpp>
#include <models/interfaces.h>
#include <views/interfaces.h>
#include <common/settings_service.hpp>
#include <common/performance_service.h>
#include <common/storage_service_interfaces.h>
#include <common/automation_service_interfaces.h>
#include <journal/common/memory_tracking_interfaces.h>
#include <app/root.h>
#include <collection.h>

namespace journal {

namespace b = baja;
namespace g = baja::graphics;
namespace a = baja::app;
namespace bt = baja::transition_tracking;
namespace jsm = journal::services::memory_tracking;
namespace jsa = journal::services::automation;

using namespace Windows::Foundation;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::Storage::Streams;
using namespace Windows::Graphics::Imaging;
using namespace Windows::Storage;
using namespace Windows::Foundation::Collections;
using namespace Windows::Storage::Pickers;
using namespace Windows::Media::Capture;

class journal_app : 
    public a::iapp,
    public a::iinput_relay,
    public g::irender_object,
    public view::icommand_observer,
    public std::enable_shared_from_this<journal_app>
{
public:
    BAJA_DECLARE_TAG("093AB4A6-EF4F-401C-AE09-6CB0838FCA8D")

private:
    class winrt_memory_blob : public view::imemory_blob
    {
    public:
        winrt_memory_blob(Platform::Array<b::byte>^ buffer) : _buffer(buffer) {}

        const b::memory* bytes()
        {
            return _buffer->Data;
        }

        size_t count()
        {
            return _buffer->Length;
        }

        Platform::Array<b::byte>^ _buffer;
    };

    ref class winrt_features
    {
    internal:
        winrt_features(const std::shared_ptr<view::ishare_source>& source, const std::shared_ptr<view::iphoto_sink>& photoSink);
        void initializeSharing();
        void copy(const bool selection);
        void paste();
        void selectFilesystemPhoto();
        void selectCameraPhoto();

    private:
        void onDataRequested(DataTransferManager^ sender, DataRequestedEventArgs^ args);
        void streamFromBlob(
            const std::shared_ptr<view::imemory_blob>& blob, 
            std::function<void(RandomAccessStreamReference^)> f
            );

        std::shared_ptr<view::ishare_source> _source;
        std::shared_ptr<view::iphoto_sink> _photoSink;
    };

public:
    journal_app(
        const std::shared_ptr<a::iloopless_app_shell>& shell,
        const std::shared_ptr<a::igraphics_factory>& graphicsFactory
        );
    ~journal_app() { this->cleanup(); }

    // graphics::irender_object
    b::int32 getPriority() { return 1; } // doesn't really matter since this is our only render object besides the consoles
    void irender_object::render(const std::shared_ptr<g::iengine>& graphics);
    void onAddedToRenderService(const std::shared_ptr<g::irender_service>& renderService) { /* noop */ }
    void onRemovedFromRenderService(const std::shared_ptr<g::irender_service>& renderService) { /* noop */ }

    // baja::app::iapp
    void initialize();
    
    // irender_observer
    void onRender();
    void onResize(const a::resize_args& args);
    void onScreenChange(const a::screen_change_args& args);
    void onVisibilityChanged(const bool visible);

    // iinput_observer
    void onPointer(const a::pointer_args& args, void* context = nullptr);
    void onGesture(const a::gesture_args& args, void* context = nullptr);
    void onKey(const a::key_args& args, void* context = nullptr);

    // isuspendable
    void onSuspend(const std::shared_ptr<a::isuspension_deferral>& deferral);

    // iinput_relay
    b::int32 addInputObserver(const std::shared_ptr<iinput_observer>& observer, void* context = nullptr) { return _inputSource += observer; }
    void removeInputObserver(const std::shared_ptr<iinput_observer>& observer) { _inputSource -= observer; }
    void removeInputObserver(const b::int32 cookie) { _inputSource -= cookie; }

    // iinput_capture
    void setCapture(const std::shared_ptr<a::iinput_observer>& observer) { _inputSource.setCapture(observer); }
    std::shared_ptr<iinput_observer> getCapture(void** context = nullptr) { return _inputSource.getCapture(context); }
    void releaseCapture() { _inputSource.releaseCapture(); }

    // icommand_observer
    void onCommand(const view::command_id id, void* context = nullptr);

    void onLaunched();

private:
    void init();
    void cleanup();
    void initializeTypes();
    void updateScreenParamsString();
    void initializeSharing();
    void onDataRequested(DataTransferManager^ sender, DataRequestedEventArgs^ args);
    void printCredits();

private:
    std::weak_ptr<a::iloopless_app_shell> _shell;
    std::weak_ptr<a::igraphics_factory> _graphicsFactory;
    std::shared_ptr<g::iengine> _graphics;
    std::shared_ptr<g::irender_service> _renderService;
    
    std::shared_ptr<a::ivar_console> _varConsole;
    std::shared_ptr<b::ivar_group> _varRoot;
    std::shared_ptr<a::idebug_console> _debugConsole;
    std::shared_ptr<b::ivar_group> _debugRoot;
    std::shared_ptr<isettings_service> _settingsService;
    std::shared_ptr<iperformance_service> _performanceService;
    std::shared_ptr<jss::istorage_service> _storageService;
    std::shared_ptr<bt::itransition_tracking_service> _undoRedoService;
    std::shared_ptr<jsa::iautomation_service> _automationService;
    std::shared_ptr<jsm::imemory_tracking_service> _memoryTrackingService;

    b::int32 _frameCount;
    std::vector<b::wchar> _screenParamsString;
    static const b::uint32 max_debug_string = 256;

    b::observable_source<a::iinput_observer> _inputSource;
    b::observable_source<a::irender_observer> _renderSource;

    std::shared_ptr<root> _root;
    std::shared_ptr<g::render_target> _renderTarget;

    winrt_features^ _winrtFeatures;
    bool _isVisible;
};

std::shared_ptr<a::iapp> createApp(
    const std::shared_ptr<a::iloopless_app_shell>& shell,
    const std::shared_ptr<a::igraphics_factory>& graphicsFactory
    );

} // namespace  journal